<?php

namespace App\Exception\Handler;

use App\Exception\ApiException;
use App\Middleware\EncryptResponseMiddleware;
use App\Utils\EncryptUtil;
use Hyperf\Di\Annotation\Inject;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\Logger\LoggerFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Throwable;

class ApiExceptionHandler extends ExceptionHandler
{
    #[Inject]
    protected \Hyperf\HttpServer\Contract\ResponseInterface $response;

    #[Inject]
    protected LoggerInterface $logger;

    public function __construct(LoggerFactory $loggerFactory)
    {
        $this->logger = $loggerFactory->get('log');
    }

    /**
     * @inheritDoc
     */
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        $returnData = [
            "code" => $throwable->getCode(),
            "msg"  => $throwable->getMessage(),
            "data" => null
        ];

        // 是否需要加密响应
        $shouldEncryptResponse = $this->routeHasEncryptResponseMiddleware(request());
        if ($shouldEncryptResponse) {

            // 加密响应数据
            $encryptedData = EncryptUtil::encrypt($returnData, get_device_identifier());

            // 阻止异常冒泡
            $this->stopPropagation();
            // 重新设置响应内容
            return $response->withAddedHeader('content-type', 'text/html')
                ->withStatus(200)->withBody(new SwooleStream($encryptedData));
        }

        // 阻止异常冒泡
        $this->stopPropagation();
        // 设置响应格式为 json
        return $response->withAddedHeader('content-type', 'application/json; charset=utf-8')
            ->withStatus(200)->withBody(new SwooleStream(
                json_encode($returnData, JSON_UNESCAPED_UNICODE)));
    }

    /**
     * 检查当前路由是否配置了加密响应中间件
     *
     * @param ServerRequestInterface $request 请求对象
     * @return bool 是否配置了加密响应中间件
     */
    protected function routeHasEncryptResponseMiddleware(ServerRequestInterface $request): bool
    {
        // 非正式环境不加密
        if (!is_prod()) {
            return false;
        }

        // 获取当前路由的 Dispatched 对象
        $dispatched = $request->getAttribute(Dispatched::class);
        if (!$dispatched || !$dispatched->isFound()) {
            return false;
        }

        // 获取路由对象
        $route = $dispatched->handler;
        if (!$route) {
            return false;
        }

        return in_array(EncryptResponseMiddleware::class, $route->options['middleware']);
    }

    /**
     * @inheritDoc
     */
    public function isValid(Throwable $throwable): bool
    {
        return $throwable instanceof ApiException;
    }
}
